package com.gitee.loyo;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.gitee.loyo.utils.CryptoUtils;
import com.gitee.loyo.utils.Factory;
import com.gitee.loyo.utils.SeqHelper;
import com.google.common.hash.Hashing;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;

import java.io.Serializable;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.Map;

import static java.nio.charset.StandardCharsets.UTF_8;

/**
 * T/CEC 102.4-2016
 * 4.5 消息主体规范
 * 接口名称：query_stations_info
 */
@Slf4j
@Getter
public class Request<T> implements Serializable {
    @JsonProperty("OperatorID")
    private String operatorId;
    @JsonProperty("Data")
    private T data;
    @JsonProperty("TimeStamp")
    private String timestamp;
    @JsonProperty("Seq")
    private String seq;
    @JsonProperty("Sig")
    private String sig;

    public Request() {
    }

    public Request(String operatorId, T data) {
        this.operatorId = operatorId;
        this.data = data;
    }

    public Request(Request request, T data){
        this.operatorId = request.operatorId;
        this.data = data;
        this.timestamp = request.timestamp;
        this.seq = request.seq;
        this.sig = request.sig;
    }

    public Map<String, String> buildHeaders(String token){
        Map<String, String> headers = new LinkedHashMap<>();
        headers.put("Content-Type", "application/json; charset=utf-8");
        if(token != null && !token.isEmpty())
            headers.put("Authorization", "Bearer " + token);
        return headers;
    }

    @SneakyThrows
    public Request<String> encryptAndSign(String dataSecretIV, String dataSecret, String sigSecret, Factory<T, String> factory){
        Request<String> newRequest = new Request<>();
        newRequest.operatorId = this.operatorId;
        newRequest.data = CryptoUtils.encodeByAes128_CBC_PKCS5Padding(factory.create(this.data), dataSecretIV, dataSecret);

        Date now = new Date();
        newRequest.timestamp = new SimpleDateFormat("yyyyMMddHHmmss").format(now);
        newRequest.seq = SeqHelper.next(now.getTime() / 1000);

        String content = newRequest.operatorId + newRequest.data + newRequest.timestamp
            + newRequest.seq;
        newRequest.sig = Hashing.hmacMd5(sigSecret.getBytes(UTF_8)).hashString(content, UTF_8).toString().toUpperCase();
        return newRequest;
    }

    public boolean checkSign(String sigSecret){
        String content = operatorId + data + timestamp + seq;
        String sig = Hashing.hmacMd5(sigSecret.getBytes(UTF_8)).hashString(content, UTF_8).toString().toUpperCase();
        log.info("this sig： {}", this.sig);
        log.info("expect sig： {}", sig);
        return sig.equalsIgnoreCase(this.sig);
    }

    @SneakyThrows
    public <T> Request<T> decrypt(String dataSecretIV, String dataSecret, Factory<String, T> factory){
        String plain = CryptoUtils.decodeByAes128_CBC_PKCS5Padding((String)data, dataSecretIV, dataSecret);
        return new Request<>(this, factory.create(plain));
    }
}
